{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Link prediction with Heterogeneous GraphSAGE (HinSAGE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "source": [
    "<table><tr><td>Run the latest release of this notebook:</td><td><a href=\"https://mybinder.org/v2/gh/stellargraph/stellargraph/master?urlpath=lab/tree/demos/link-prediction/hinsage-link-prediction.ipynb\" alt=\"Open In Binder\" target=\"_parent\"><img src=\"https://mybinder.org/badge_logo.svg\"/></a></td><td><a href=\"https://colab.research.google.com/github/stellargraph/stellargraph/blob/master/demos/link-prediction/hinsage-link-prediction.ipynb\" alt=\"Open In Colab\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\"/></a></td></tr></table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this example, we use our generalisation of the [GraphSAGE](http://snap.stanford.edu/graphsage/) algorithm to heterogeneous graphs (which we call HinSAGE) to build a model that predicts user-movie ratings in the MovieLens dataset (see below). The problem is treated as a supervised link attribute inference problem on a user-movie network with nodes of two types (users and movies, both attributed) and links corresponding to user-movie ratings, with integer `rating` attributes from 1 to 5 (note that if a user hasn't rated a movie, the corresponding user-movie link does not exist in the network).\n",
    "\n",
    "To address this problem, we build a model with the following architecture: a two-layer HinSAGE model that takes labeled `(user, movie)` node pairs corresponding to user-movie ratings, and outputs a pair of node embeddings for the `user` and `movie` nodes of the pair. These embeddings are then fed into a link regression layer, which applies a binary operator to those node embeddings (e.g., concatenating them) to construct the link embedding. Thus obtained link embeddings are passed through the link regression layer to obtain predicted user-movie ratings. The entire model is trained end-to-end by minimizing the loss function of choice (e.g., root mean square error between predicted and true ratings) using stochastic gradient descent (SGD) updates of the model parameters, with minibatches of user-movie training links fed into the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "outputs": [],
   "source": [
    "# install StellarGraph if running on Google Colab\n",
    "import sys\n",
    "if 'google.colab' in sys.modules:\n",
    "  %pip install -q stellargraph[demos]==1.2.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "VersionCheck"
    ]
   },
   "outputs": [],
   "source": [
    "# verify that we're using the correct version of StellarGraph for this notebook\n",
    "import stellargraph as sg\n",
    "\n",
    "try:\n",
    "    sg.utils.validate_notebook_version(\"1.2.1\")\n",
    "except AttributeError:\n",
    "    raise ValueError(\n",
    "        f\"This notebook requires StellarGraph version 1.2.1, but a different version {sg.__version__} is installed.  Please see <https://github.com/stellargraph/stellargraph/issues/1172>.\"\n",
    "    ) from None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn import preprocessing, feature_extraction, model_selection\n",
    "from sklearn.metrics import mean_absolute_error, mean_squared_error\n",
    "\n",
    "import stellargraph as sg\n",
    "from stellargraph.mapper import HinSAGELinkGenerator\n",
    "from stellargraph.layer import HinSAGE, link_regression\n",
    "from tensorflow.keras import Model, optimizers, losses, metrics\n",
    "\n",
    "import multiprocessing\n",
    "from stellargraph import datasets\n",
    "from IPython.display import display, HTML\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Specify the minibatch size (number of user-movie links per minibatch) and the number of epochs for training the ML model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "batch_size = 200\n",
    "epochs = 20\n",
    "# Use 70% of edges for training, the rest for testing:\n",
    "train_size = 0.7\n",
    "test_size = 0.3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load the dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "DataLoadingLinks"
    ]
   },
   "source": [
    "(See [the \"Loading from Pandas\" demo](../basics/loading-pandas.ipynb) for details on how data can be loaded.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": [
     "DataLoading"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "The MovieLens 100K dataset contains 100,000 ratings from 943 users on 1682 movies."
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dataset = datasets.MovieLens()\n",
    "display(HTML(dataset.description))\n",
    "G, edges_with_ratings = dataset.load()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "StellarGraph: Undirected multigraph\n",
      " Nodes: 2625, Edges: 100000\n",
      "\n",
      " Node types:\n",
      "  movie: [1682]\n",
      "    Features: float32 vector, length 19\n",
      "    Edge types: movie-rating->user\n",
      "  user: [943]\n",
      "    Features: float32 vector, length 24\n",
      "    Edge types: user-rating->movie\n",
      "\n",
      " Edge types:\n",
      "    user-rating->movie: [100000]\n"
     ]
    }
   ],
   "source": [
    "print(G.info())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Split the edges into train and test sets for model training/evaluation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "edges_train, edges_test = model_selection.train_test_split(\n",
    "    edges_with_ratings, train_size=train_size, test_size=test_size\n",
    ")\n",
    "\n",
    "edgelist_train = list(edges_train[[\"user_id\", \"movie_id\"]].itertuples(index=False))\n",
    "edgelist_test = list(edges_test[[\"user_id\", \"movie_id\"]].itertuples(index=False))\n",
    "\n",
    "labels_train = edges_train[\"rating\"]\n",
    "labels_test = edges_test[\"rating\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our machine learning task of learning user-movie ratings can be framed as a supervised Link Attribute Inference: given a graph of user-movie ratings, we train a model for rating prediction using the ratings edges_train, and evaluate it using the test ratings edges_test. The model also requires the user-movie graph structure, to do the neighbour sampling required by the HinSAGE algorithm."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We create the link mappers for sampling and streaming training and testing data to the model. The link mappers essentially \"map\" user-movie links to the input of HinSAGE: they take minibatches of user-movie links, sample 2-hop subgraphs of G with `(user, movie)` head nodes extracted from those user-movie links, and feed them, together with the corresponding user-movie ratings, to the input layer of the HinSAGE model, for SGD updates of the model parameters."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Specify the sizes of 1- and 2-hop neighbour samples for HinSAGE:\n",
    "\n",
    "Note that the length of `num_samples` list defines the number of layers/iterations in the HinSAGE model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_samples = [8, 4]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create the generators to feed data from the graph to the Keras model. We need to specify the nodes types for the user-movie pairs that we will feed to the model. The `shuffle=True` argument is given to the `flow` method to improve training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "generator = HinSAGELinkGenerator(\n",
    "    G, batch_size, num_samples, head_node_types=[\"user\", \"movie\"]\n",
    ")\n",
    "train_gen = generator.flow(edgelist_train, labels_train, shuffle=True)\n",
    "test_gen = generator.flow(edgelist_test, labels_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Build the model by stacking a two-layer HinSAGE model and a link regression layer on top.\n",
    "\n",
    "First, we define the HinSAGE part of the model, with hidden layer sizes of 32 for both HinSAGE layers, a bias term, and no dropout. (Dropout can be switched on by specifying a positive `dropout` rate, `0 < dropout < 1`)\n",
    "\n",
    "Note that the length of `layer_sizes` list must be equal to the length of `num_samples`, as `len(num_samples)` defines the number of hops (layers) in the HinSAGE model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('user', [2]),\n",
       " ('movie', [3]),\n",
       " ('movie', [4]),\n",
       " ('user', [5]),\n",
       " ('user', []),\n",
       " ('movie', [])]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generator.schema.type_adjacency_list(generator.head_node_types, len(num_samples))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'user': [EdgeType(n1='user', rel='rating', n2='movie')],\n",
       " 'movie': [EdgeType(n1='movie', rel='rating', n2='user')]}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generator.schema.schema"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "hinsage_layer_sizes = [32, 32]\n",
    "assert len(hinsage_layer_sizes) == len(num_samples)\n",
    "\n",
    "hinsage = HinSAGE(\n",
    "    layer_sizes=hinsage_layer_sizes, generator=generator, bias=True, dropout=0.0\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Expose input and output sockets of hinsage:\n",
    "x_inp, x_out = hinsage.in_out_tensors()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Add the final estimator layer for predicting the ratings. The edge_embedding_method argument specifies the way in which node representations (node embeddings) are combined into link representations (recall that links represent user-movie ratings, and are thus pairs of (user, movie) nodes). In this example, we will use `concat`, i.e., node embeddings are concatenated to get link embeddings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "link_regression: using 'concat' method to combine node embeddings into edge embeddings\n"
     ]
    }
   ],
   "source": [
    "# Final estimator layer\n",
    "score_prediction = link_regression(edge_embedding_method=\"concat\")(x_out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create the Keras model, and compile it by specifying the optimizer, loss function to optimise, and metrics for diagnostics:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow.keras.backend as K\n",
    "\n",
    "\n",
    "def root_mean_square_error(s_true, s_pred):\n",
    "    return K.sqrt(K.mean(K.pow(s_true - s_pred, 2)))\n",
    "\n",
    "\n",
    "model = Model(inputs=x_inp, outputs=score_prediction)\n",
    "model.compile(\n",
    "    optimizer=optimizers.Adam(lr=1e-2),\n",
    "    loss=losses.mean_squared_error,\n",
    "    metrics=[root_mean_square_error, metrics.mae],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Summary of the model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"model\"\n",
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_3 (InputLayer)            [(None, 8, 19)]      0                                            \n",
      "__________________________________________________________________________________________________\n",
      "input_5 (InputLayer)            [(None, 32, 24)]     0                                            \n",
      "__________________________________________________________________________________________________\n",
      "input_6 (InputLayer)            [(None, 32, 19)]     0                                            \n",
      "__________________________________________________________________________________________________\n",
      "input_1 (InputLayer)            [(None, 1, 24)]      0                                            \n",
      "__________________________________________________________________________________________________\n",
      "reshape (Reshape)               (None, 1, 8, 19)     0           input_3[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "reshape_2 (Reshape)             (None, 8, 4, 24)     0           input_5[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "input_4 (InputLayer)            [(None, 8, 24)]      0                                            \n",
      "__________________________________________________________________________________________________\n",
      "reshape_3 (Reshape)             (None, 8, 4, 19)     0           input_6[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_1 (Dropout)             (None, 1, 24)        0           input_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout (Dropout)               (None, 1, 8, 19)     0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_5 (Dropout)             (None, 8, 19)        0           input_3[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_4 (Dropout)             (None, 8, 4, 24)     0           reshape_2[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "input_2 (InputLayer)            [(None, 1, 19)]      0                                            \n",
      "__________________________________________________________________________________________________\n",
      "reshape_1 (Reshape)             (None, 1, 8, 24)     0           input_4[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_7 (Dropout)             (None, 8, 24)        0           input_4[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_6 (Dropout)             (None, 8, 4, 19)     0           reshape_3[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "mean_hin_aggregator (MeanHinAgg multiple             720         dropout_1[0][0]                  \n",
      "                                                                 dropout[0][0]                    \n",
      "                                                                 dropout_7[0][0]                  \n",
      "                                                                 dropout_6[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "mean_hin_aggregator_1 (MeanHinA multiple             720         dropout_3[0][0]                  \n",
      "                                                                 dropout_2[0][0]                  \n",
      "                                                                 dropout_5[0][0]                  \n",
      "                                                                 dropout_4[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "dropout_3 (Dropout)             (None, 1, 19)        0           input_2[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dropout_2 (Dropout)             (None, 1, 8, 24)     0           reshape_1[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "reshape_4 (Reshape)             (None, 1, 8, 32)     0           mean_hin_aggregator_1[1][0]      \n",
      "__________________________________________________________________________________________________\n",
      "reshape_5 (Reshape)             (None, 1, 8, 32)     0           mean_hin_aggregator[1][0]        \n",
      "__________________________________________________________________________________________________\n",
      "dropout_9 (Dropout)             (None, 1, 32)        0           mean_hin_aggregator[0][0]        \n",
      "__________________________________________________________________________________________________\n",
      "dropout_8 (Dropout)             (None, 1, 8, 32)     0           reshape_4[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "dropout_11 (Dropout)            (None, 1, 32)        0           mean_hin_aggregator_1[0][0]      \n",
      "__________________________________________________________________________________________________\n",
      "dropout_10 (Dropout)            (None, 1, 8, 32)     0           reshape_5[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "mean_hin_aggregator_2 (MeanHinA (None, 1, 32)        1056        dropout_9[0][0]                  \n",
      "                                                                 dropout_8[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "mean_hin_aggregator_3 (MeanHinA (None, 1, 32)        1056        dropout_11[0][0]                 \n",
      "                                                                 dropout_10[0][0]                 \n",
      "__________________________________________________________________________________________________\n",
      "reshape_6 (Reshape)             (None, 32)           0           mean_hin_aggregator_2[0][0]      \n",
      "__________________________________________________________________________________________________\n",
      "reshape_7 (Reshape)             (None, 32)           0           mean_hin_aggregator_3[0][0]      \n",
      "__________________________________________________________________________________________________\n",
      "lambda (Lambda)                 (None, 32)           0           reshape_6[0][0]                  \n",
      "                                                                 reshape_7[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "link_embedding (LinkEmbedding)  (None, 64)           0           lambda[0][0]                     \n",
      "                                                                 lambda[1][0]                     \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 1)            65          link_embedding[0][0]             \n",
      "__________________________________________________________________________________________________\n",
      "reshape_8 (Reshape)             (None, 1)            0           dense[0][0]                      \n",
      "==================================================================================================\n",
      "Total params: 3,617\n",
      "Trainable params: 3,617\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Specify the number of workers to use for model training\n",
    "num_workers = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate the fresh (untrained) model on the test set (for reference):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  ['...']\n",
      "150/150 [==============================] - 25s 165ms/step - loss: 11.8834 - root_mean_square_error: 3.4467 - mean_absolute_error: 3.256210s - loss: 11.8462 - root_mean_square_erro\n",
      "Untrained model's Test Evaluation:\n",
      "\tloss: 11.8834\n",
      "\troot_mean_square_error: 3.4467\n",
      "\tmean_absolute_error: 3.2562\n"
     ]
    }
   ],
   "source": [
    "test_metrics = model.evaluate(\n",
    "    test_gen, verbose=1, use_multiprocessing=False, workers=num_workers\n",
    ")\n",
    "\n",
    "print(\"Untrained model's Test Evaluation:\")\n",
    "for name, val in zip(model.metrics_names, test_metrics):\n",
    "    print(\"\\t{}: {:0.4f}\".format(name, val))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Train the model by feeding the data from the graph in minibatches, using mapper_train, and get validation metrics after each epoch:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  ['...']\n",
      "  ['...']\n",
      "Train for 350 steps, validate for 150 steps\n",
      "Epoch 1/20\n",
      "350/350 [==============================] - 87s 249ms/step - loss: 1.3311 - root_mean_square_error: 1.1321 - mean_absolute_error: 0.9363 - val_loss: 1.1607 - val_root_mean_square_error: 1.0759 - val_mean_absolute_error: 0.8687error: 1.13\n",
      "Epoch 2/20\n",
      "350/350 [==============================] - 85s 244ms/step - loss: 1.1525 - root_mean_square_error: 1.0724 - mean_absolute_error: 0.8711 - val_loss: 1.1315 - val_root_mean_square_error: 1.0624 - val_mean_absolute_error: 0.8693s -  - ETA: 2\n",
      "Epoch 3/20\n",
      "350/350 [==============================] - 90s 256ms/step - loss: 1.1357 - root_mean_square_error: 1.0645 - mean_absolute_error: 0.8626 - val_loss: 1.1141 - val_root_mean_square_error: 1.0542 - val_mean_absolute_error: 0.8612 loss: 1.1321 - root_mean_square_error: 1.0628 - mean_absolute_error: - ETA: 10s - loss: \n",
      "Epoch 4/20\n",
      "350/350 [==============================] - 83s 236ms/step - loss: 1.1235 - root_mean_square_error: 1.0588 - mean_absolute_error: 0.8566 - val_loss: 1.1035 - val_root_mean_square_error: 1.0489 - val_mean_absolute_error: 0.8432\n",
      "Epoch 5/20\n",
      "350/350 [==============================] - 89s 255ms/step - loss: 1.1148 - root_mean_square_error: 1.0546 - mean_absolute_error: 0.8526 - val_loss: 1.1057 - val_root_mean_square_error: 1.0500 - val_mean_absolute_error: 0.8372are_error: 1.05 - ETA: 21s - loss: 1.1116 - root_mean_square_error: 1.0531 - mean_abso - E - ETA: 4s - loss: 1.1130 - root_mean_square_error: 1.0537 - mean_abso\n",
      "Epoch 6/20\n",
      "350/350 [==============================] - 85s 243ms/step - loss: 1.1123 - root_mean_square_error: 1.0533 - mean_absolute_error: 0.8517 - val_loss: 1.0983 - val_root_mean_square_error: 1.0466 - val_mean_absolute_error: 0.8461\n",
      "Epoch 7/20\n",
      "350/350 [==============================] - 100s 286ms/step - loss: 1.1096 - root_mean_square_error: 1.0523 - mean_absolute_error: 0.8498 - val_loss: 1.0966 - val_root_mean_square_error: 1.0458 - val_mean_absolute_error: 0.8538ot_me - ETA: 39s - loss: 1.1068 - root_mean_square_error: 1.0510 - mean_absolu - E - ETA: 18s - loss: 1.1056 - root_mean_square_error: 1.0504 - mean_absolu - ETA: 14s - loss: 1.1078 - ETA: 6s - loss: 1.1085 - root_mean_square_e\n",
      "Epoch 8/20\n",
      "350/350 [==============================] - 91s 261ms/step - loss: 1.1049 - root_mean_square_error: 1.0502 - mean_absolute_error: 0.8478 - val_loss: 1.0947 - val_root_mean_square_error: 1.0449 - val_mean_absolute_error: 0.8489 1:44 - loss: 1.0948 - root_mean_square_error: 1.0454 - mean_absolute_error: 0. - ETA: 1:42 - loss: 1.0966 - root_mean_square_error: 1.04 - ETA: 1:32 - loss: 1.0876 - root_mean_square_error: 1.0419 - mean_absolute_error - ETA: 1:30 - loss: 1.0922 - root_mean_square_error: 1.04 - ETA: 1:19 - loss: 1.0931 - root_mean_squa - ETA: 1:05 - loss: 1.0975 - root_mean_square_error: 1. - ETA: 54s - loss: 1.0982 - root_mean_square_error - ETA: 40s - loss: 1.1085 - root_mean_square_e - ETA: 27s - loss: 1.1068 - root_mean_square_error: 1.0511 - mean_absolute_error:  - ETA: 25s - loss: 1.1083 - root - ETA: 12s - loss: \n",
      "Epoch 9/20\n",
      "350/350 [==============================] - 79s 225ms/step - loss: 1.1031 - root_mean_square_error: 1.0490 - mean_absolute_error: 0.8473 - val_loss: 1.0926 - val_root_mean_square_error: 1.0439 - val_mean_absolute_error: 0.8503\n",
      "Epoch 10/20\n",
      "350/350 [==============================] - 79s 225ms/step - loss: 1.0967 - root_mean_square_error: 1.0461 - mean_absolute_error: 0.8439 - val_loss: 1.0842 - val_root_mean_square_error: 1.0399 - val_mean_absolute_error: 0.8402root_mean_squa - ETA: 28s - loss: 1.0982 - root_mean_square_error: 1.0467 - mean_absolute_err - ETA: 26s - loss: 1  - ETA: 3s - loss: 1.0969 - root_mean_square_error: 1.0461 - mean_absolu\n",
      "Epoch 11/20\n",
      "350/350 [==============================] - 79s 226ms/step - loss: 1.0955 - root_mean_square_error: 1.0455 - mean_absolute_error: 0.8437 - val_loss: 1.0832 - val_root_mean_square_error: 1.0394 - val_mean_absolute_error: 0.8402\n",
      "Epoch 12/20\n",
      "350/350 [==============================] - 104s 298ms/step - loss: 1.0948 - root_mean_square_error: 1.0453 - mean_absolute_error: 0.8426 - val_loss: 1.0866 - val_root_mean_square_error: 1.0411 - val_mean_absolute_error: 0.8448square_error: 1.0466 - m - ETA: 6s - loss: 1.0970 - root_mean_square_error: 1.0463 - mean_ - ETA: 4s - loss: 1.0953 - root_mean_square_error: 1.0455 - mean_absolu\n",
      "Epoch 13/20\n",
      "350/350 [==============================] - 132s 377ms/step - loss: 1.0941 - root_mean_square_error: 1.0448 - mean_absolute_error: 0.8431 - val_loss: 1.0867 - val_root_mean_square_error: 1.0410 - val_mean_absolute_error: 0.8492 loss: 1.0591 - root_mean_square_error: 1.0280 - mean_absolute_error:  - ETA: 3:08 - loss: 1.0806 - root_mean_square_error: 1.0383 - mean_absolu - ETA: 2:31 - loss: 1.0934 - root_mean_square_error: 1.0448 - mean_absolute_error:  - ETA: 2:22 - loss: 1.093 - ETA: 1:38 - loss: 1.0824 - root_mean_square_error: 1.03 - ETA: 1:27 - loss: 1.0839 - root_mean_square_error: 1.0401 - ETA: 1:20 - loss: 1.0836 - root_mean_square_error - ETA: 51s - loss: 1.0905 - root_mean_square_error: 1.0432 - me - ETA: 44s - loss: 1.0910 - root_mean_square_error: 1.0433 - mean_absolute_error: 0. - ETA: 42s - loss: 1.0926 - root_mean_squa - ETA: 29s - loss: 1.0936 - root_mean_square_error: 1.0445 - mean - ETA: 22s - loss: 1.0910 - root_mean_square_error: 1.0433 - mean_absolute_error: 0.842 - ETA: 22s - loss: 1.0905 - root_mean_square_error: 1.0430 - mean_absolute_error:  - ETA: 20s - loss: 1.0901 - root_mean_square_error: 1.0429 - mean_absolute_error: 0. - ETA: 19s - loss: 1.0899 - root_mean_square_error: 1 - ETA: 9s - loss: 1.0915 - root_mean_square_\n",
      "Epoch 14/20\n",
      "350/350 [==============================] - 109s 311ms/step - loss: 1.0895 - root_mean_square_error: 1.0426 - mean_absolute_error: 0.8408 - val_loss: 1.0872 - val_root_mean_square_error: 1.0411 - val_mean_absolute_error: 0.8332square_error:  - ETA: 1:02 - loss: 1.0780 - root_mean_square_error: 1.0371 - mean_absolute_error: 0. - ETA: 1:02 - loss: 1.0764 - root_mean_square_error: 1.0364 - mean_a - ETA: 58s - loss: 1.0809 - root_mean_square_error: 1.0386 - mean_abso - ETA: 53s - loss: 1.0807 - root_mean_square_error: 1.0384 - m - ETA: 46s - loss: 1.0810 - r - ETA: 26s - loss: 1.0835 - root_mean_square_ - ETA: 14s - loss: 1.0863 - root_mean_square_error: 1.0410 - mean_absolute_error: 0 - ETA: 13s -\n",
      "Epoch 15/20\n",
      "350/350 [==============================] - 100s 286ms/step - loss: 1.0880 - root_mean_square_error: 1.0419 - mean_absolute_error: 0.8412 - val_loss: 1.0884 - val_root_mean_square_error: 1.0418 - val_mean_absolute_error: 0.8358absolute_error: 0.84 - ETA: 28s - loss: 1.0879 - root_mean_square_error: 1.0419 - mean_absolute_error: 0.84 - ETA: 28s - loss: 1.0876 -  - ETA: 11s - loss: 1.0885 - root_mean_square_error: 1.0421 - mean_absolute_error: 0.8 - ETA: 11s - loss: 1.0877 - root_mean_square_error: 1.0417 - mean_absol - ETA: 8s - loss: 1.0888 - root_mean_square_err\n",
      "Epoch 16/20\n",
      "350/350 [==============================] - 82s 234ms/step - loss: 1.0857 - root_mean_square_error: 1.0409 - mean_absolute_error: 0.8401 - val_loss: 1.0833 - val_root_mean_square_error: 1.0392 - val_mean_absolute_error: 0.8332\n",
      "Epoch 17/20\n",
      "350/350 [==============================] - 87s 247ms/step - loss: 1.0837 - root_mean_square_error: 1.0398 - mean_absolute_error: 0.8387 - val_loss: 1.0744 - val_root_mean_square_error: 1.0351 - val_mean_absolute_error: 0.8348\n",
      "Epoch 18/20\n",
      "350/350 [==============================] - 88s 251ms/step - loss: 1.0840 - root_mean_square_error: 1.0400 - mean_absolute_error: 0.8389 - val_loss: 1.0878 - val_root_mean_square_error: 1.0416 - val_mean_absolute_error: 0.8527_square_error: 1.0385 - mean_absolute_erro - ETA: 17s - - ETA: 5s - loss: 1.0813 - root_mean_square_error: 1.0387 -\n",
      "Epoch 19/20\n",
      "350/350 [==============================] - 83s 236ms/step - loss: 1.0798 - root_mean_square_error: 1.0380 - mean_absolute_error: 0.8371 - val_loss: 1.0728 - val_root_mean_square_error: 1.0344 - val_mean_absolute_error: 0.8424\n",
      "Epoch 20/20\n",
      "350/350 [==============================] - 79s 227ms/step - loss: 1.0815 - root_mean_square_error: 1.0386 - mean_absolute_error: 0.8387 - val_loss: 1.0680 - val_root_mean_square_error: 1.0319 - val_mean_absolute_error: 0.83024s - loss: 1.0764 - root_mean_square_error: 1.0362 - me - ETA: 38s - loss: 1.0777 - root_\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(\n",
    "    train_gen,\n",
    "    validation_data=test_gen,\n",
    "    epochs=epochs,\n",
    "    verbose=1,\n",
    "    shuffle=False,\n",
    "    use_multiprocessing=False,\n",
    "    workers=num_workers,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Plot the training history:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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G9i9Cd05x80338y4ceNIT09n/Pjx3HLLLTvdq6KigiuvvJJLLrmEwsJCRo4cyQ9+8AM6OjrC+wfsp5gGuHNuLlCzh+MNbsfyaFlA1/bngeecczXOuVrgOfb8h0DUmBllBQGNBRcR6cMpp5xCW1sbTz755E7777//fr75zW9iZowYMYKHHnqIpUuXcsstt3Dvvfdy3XXX9fse69atY+bMmRx66KG8/fbb/PrXv+aSSy7Z7Ty/38+dd97JkiVLmDNnDi+++CLf+973AK9Z/rbbbgNg/fr1rF+/ntmzZ/d6vzvuuIOf/exnXHbZZbz//vv86Ec/4rLLLuMPf/jDTufdeuutjB49mjfffJNbb72V2267jfvuu6/fvyscqXs/JbbM7MvA9cAI4Iuh3WOBtT1Oqwrt6+37s4BZAOXl5VEpY1lhQGPBRST2nrkMNiyO/X1HHQIn3tDv0/Py8jj55JO5//77OfXUUwGYP38+S5Ys4fHHH8fn83Httdd2n19RUcHKlSu54447uOaaa/p1jzvuuIPi4mLuuusuUlNTOfDAA7nuuus46aSTdjrvpz/96U73uf766znttNO49957SU9PJy8vz/uJo0bt8X433HADF198MbNmzQJgwoQJLFu2jGuvvZZvf/vb3ecdddRRXHbZZd3n3Hvvvfz73//e6ZxIGXKd2JxzTzjn9gf+C/jFAL5/p3NuunNueklJSeQLiDcWfG1NE8m8lrqIyGCcddZZPPvss2zatAnwat8zZsxg4sSJgNfMfvjhhzNy5Eiys7O5/PLL+eijj/p9/SVLljBjxoydeqofeeSRu533+OOPc/TRRzNmzBiys7M544wzaGtrY8OGDf2+V319PVVVVRx99NE77T/mmGNYs2YNTU07WmSnTJmy0zljxoxh48aN/b5XOIZcDbyLc26umY0zs2KgGji2x+FS4MV4lAugtMBPQ2sH25raKchKj1cxRGS4CaMWHG/HH388xcXFPPTQQ1x44YU8/PDDXH311QA8+uijXHjhhdxwww0cc8wx5Obm8uijj3LFFVdEtAxvvvkmX//617n88su56aabKCgo4I033uCss86ira0tovfqkp6+cyaYGcFgMCr3GlI1cDPb10L9+c1sGpABbAX+BRxvZgWhzmvHh/bFhYaSiYjsWUpKCmeccQZ//OMfeeaZZ6irq+O0004DYO7c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\n",
      "text/plain": [
       "<Figure size 504x864 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sg.utils.plot_history(history)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate the trained model on test user-movie rankings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  ['...']\n",
      "150/150 [==============================] - 23s 152ms/step - loss: 1.0738 - root_mean_square_error: 1.0347 - mean_absolute_error: 0.8325 44s - loss: 1.0911 - root_mean_square\n",
      "Test Evaluation:\n",
      "\tloss: 1.0738\n",
      "\troot_mean_square_error: 1.0347\n",
      "\tmean_absolute_error: 0.8325\n"
     ]
    }
   ],
   "source": [
    "test_metrics = model.evaluate(\n",
    "    test_gen, use_multiprocessing=False, workers=num_workers, verbose=1\n",
    ")\n",
    "\n",
    "print(\"Test Evaluation:\")\n",
    "for name, val in zip(model.metrics_names, test_metrics):\n",
    "    print(\"\\t{}: {:0.4f}\".format(name, val))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compare the predicted test rankings with \"mean baseline\" rankings, to see how much better our model does compared to this (very simplistic) baseline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean Baseline Test set metrics:\n",
      "\troot_mean_square_error =  1.1250835578845824\n",
      "\tmean_absolute_error =  0.9442556604067485\n",
      "\n",
      "Model Test set metrics:\n",
      "\troot_mean_square_error =  1.0363648722884464\n",
      "\tmean_absolute_error =  0.8332940764943758\n"
     ]
    }
   ],
   "source": [
    "y_true = labels_test\n",
    "# Predict the rankings using the model:\n",
    "y_pred = model.predict(test_gen)\n",
    "# Mean baseline rankings = mean movie ranking:\n",
    "y_pred_baseline = np.full_like(y_pred, np.mean(y_true))\n",
    "\n",
    "rmse = np.sqrt(mean_squared_error(y_true, y_pred_baseline))\n",
    "mae = mean_absolute_error(y_true, y_pred_baseline)\n",
    "print(\"Mean Baseline Test set metrics:\")\n",
    "print(\"\\troot_mean_square_error = \", rmse)\n",
    "print(\"\\tmean_absolute_error = \", mae)\n",
    "\n",
    "rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "mae = mean_absolute_error(y_true, y_pred)\n",
    "print(\"\\nModel Test set metrics:\")\n",
    "print(\"\\troot_mean_square_error = \", rmse)\n",
    "print(\"\\tmean_absolute_error = \", mae)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compare the distributions of predicted and true rankings for the test set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "h_true = plt.hist(y_true, bins=30, facecolor=\"green\", alpha=0.5)\n",
    "h_pred = plt.hist(y_pred, bins=30, facecolor=\"blue\", alpha=0.5)\n",
    "plt.xlabel(\"ranking\")\n",
    "plt.ylabel(\"count\")\n",
    "plt.legend((\"True\", \"Predicted\"))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that our model beats the \"mean baseline\" by a significant margin. To further improve the model, you can try increasing the number of training epochs, change the dropout rate, change the sample sizes for subgraph sampling `num_samples`, hidden layer sizes `layer_sizes` of the HinSAGE part of the model, or try increasing the number of HinSAGE layers.\n",
    "\n",
    "However, note that the distribution of predicted scores is still very narrow, and rarely gives 1, 2 or 5 as a score."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This model uses a bipartite user-movie graph to learn to predict movie ratings. It can be further enhanced by using additional relations, e.g., friendships between users, if they become available. And the best part is: the underlying algorithm of the model does not need to change at all to take these extra relations into account - all that changes is the graph that it learns from!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "source": [
    "<table><tr><td>Run the latest release of this notebook:</td><td><a href=\"https://mybinder.org/v2/gh/stellargraph/stellargraph/master?urlpath=lab/tree/demos/link-prediction/hinsage-link-prediction.ipynb\" alt=\"Open In Binder\" target=\"_parent\"><img src=\"https://mybinder.org/badge_logo.svg\"/></a></td><td><a href=\"https://colab.research.google.com/github/stellargraph/stellargraph/blob/master/demos/link-prediction/hinsage-link-prediction.ipynb\" alt=\"Open In Colab\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\"/></a></td></tr></table>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
